<?php
namespace App\Http\Controllers;

use App\Models\FileOperate;
use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;
use GetId3\GetId3Core as GetId3;
use App\Models\Storage;
use App\Models\UserFile;
use Auth;
use Maatwebsite\Excel\Classes\Cache;

/**
 * 文件管理器
 *
 * @author Latrell Chan
 */
class StorageController extends Controller
{

	/**
	 * 文件存储库根路径。
	 */
	protected $storage_path;

	/**
	 * 文件上传字段名。
	 */
	protected $filed_name = 'body';

	public function __construct()
	{
 		$this->middleware('auth.token', [
 			'except' => [
 				'getFile','postFile','postFileWithHash','postKeditFile','postPluploadFile','postCkedit'
 			]
 		]);

		$this->storage_path = base_path('storage/files') . DIRECTORY_SEPARATOR;
	}

	/**
	 * 获取文件
	 */
	public function getFile(Request $request)
	{
		// 验证数据。
		$this->validate($request, [
            'id' => [
                'required_without:hash',
                'exists:user_files,id'
            ],
            'hash' => [
                'required_without:id',
                'exists:storage,hash'
            ]
		]);

		// 取得文件模型。
        if ($request->has('id')) {
//            $file_key = 'file_key_v1_'.$request->input('id');
//            if(Cache::has($file_key)){
//                $file = Cache::get($file_key);
//            }else {
                $file = UserFile::find($request->input('id'));
//                Cache::put($file_key,$file,100);
//            }
//            $storage_key = 'storage_key_v1_'.$request->input('id');
//            if(Cache::has($storage_key)){
//                $storage = Cache::get($storage_key);
//            }else {
                $storage = $file->storage;
//                Cache::put($storage_key,$storage,100);
//            }

            // 取得文件名
            $filename = $file->filename;
        } else if ($request->has('hash')) {
            $storage = Storage::find($request->get('hash'));
            // 取得文件名
            $filename = $storage->hash;
        }

		// 取得文件拓展名。
		$extension = '.' . $storage->format;
		$disposition = 'attachment';
		$headers = [
			'Content-Type' => $storage->mime
		];

		// 根据文件Mime处理额外操作。
		switch ($storage->mime) {

			// 图片。
			case 'image/jpeg':
			case 'image/png':
			case 'image/gif':
			case 'image/tiff':

				// 取得欲取的图片尺寸。
				$width = $request->input('width', 0);
				$height = $request->input('height', 0);
				$width = $width > 0 ? ceil($width) : null;
				$height = $height > 0 ? ceil($height) : null;

				// 缩放方式。
				// Enforce:强制压缩。图片会变形。
				// Fit:中心裁剪。裁剪出指定尺寸的最大区域。（默认）
				// Inside:里框压缩。取最短边缩放。
				// Outside:外框压缩。取最长边缩放。
				$method = 'fit';
				if ($width > 0 && $height > 0) {
					// 除了裁切以外，其它的缩放方式需要同时提供宽高参数。
					switch ($request->input('mode')) {
						case 'Enforce':
							$method = 'resize';
							break;
						default:
						case 'Fit':
							break;
						case 'Inside':
							if ($storage->width < $storage->height) {
								if ($storage->width > $width) {
									$height = floor($width / $storage->width * $storage->height);
								}
							} else {
								if ($storage->height > $height) {
									$width = floor($height / $storage->height * $storage->width);
								}
							}

							break;
						case 'Outside':
							if ($storage->width > $storage->height) {
								if ($storage->width > $width) {
									$height = floor($width / $storage->width * $storage->height);
								}
							} else {
								if ($storage->height > $height) {
									$width = floor($height / $storage->height * $storage->width);
								}
							}
							break;
					}
				}

				// 如果是要取缩略图。
				if ((! is_null($width) || ! is_null($height)) && ($storage->width != $width || $storage->height != $height)) {
					// 查找当前图片的缩略图列表，是否已经有此尺寸的图片。
					if (is_null($width) && ! is_null($height)) {
						$width = ceil($storage->width / $storage->height * $height);
					}
					if (is_null($height) && ! is_null($width)) {
						$height = ceil($storage->height / $storage->width * $width);
					}
					$thumbnail = $storage->thumbnails()
						->where('width', $width)
						->where('height', $height)
						->first();

					// 如果指定尺寸的缩略图不存在，则生成并保存到数据库中。
					if (is_null($thumbnail)) {

						// 在缓存目录内创建一个pic前缀的临时文件，并修改推展名。
						$tmp = tempnam(storage_path('cache'), 'pic');
						rename($tmp, $tmp .= '.jpg');

						// 打开原图。
						$image = Image::make($this->storage_path . $storage->path);
						// 生成一张需求尺寸的图片，写到创建的临时文件内。
						// $image->resize($width, $height);
						$image->fit($width, $height);
						$image->save($tmp, 60);

						// 计算文件的hash，并移动文件到文件存储目录。
						$_hash = md5_file($tmp);
						$_filename = $_hash . '.' . $image->extension;
						$_storage = $this->storage_path . $_filename;
						copy($tmp, $_storage);
						unlink($tmp);

						// 保存图片信息到数据库。
						$thumbnail = Storage::find($_hash);
						if (is_null($thumbnail)) {
							$thumbnail = new Storage();
							$thumbnail->hash = $_hash;
						}
						$thumbnail->size = filesize($_storage);
						$thumbnail->width = $image->width();
						$thumbnail->height = $image->height();
						$thumbnail->mime = $image->mime;
						$thumbnail->format = 'jpeg';
						$thumbnail->path = $_filename;
						$thumbnail->save();
						$storage->thumbnails()->attach($_hash);
					}

					// 此次请求使用此缩略图。
					$storage = $thumbnail;
					// $filename .= "_{$storage->width}_{$storage->height}";
					$extension = $storage->format;
				}

				$disposition = 'inline';
				break;

			// 视频。
			case 'video/x-ms-asf':
			case 'video/x-ms-asf':
			case 'video/avi':
			case 'video/x-ivf':
			case 'video/x-mpeg':
			case 'video/x-mpeg':
			case 'video/mpeg4':
			case 'video/x-sgi-movie':
			case 'video/mpeg':
			case 'video/mpeg4':
			case 'video/x-mpg':
			case 'video/x-mpeg':
			case 'video/mpg':
			case 'video/mpg':
			case 'video/x-mpeg':
			case 'video/mpg':
			case 'video/mpeg':
			case 'video/vnd.rn-realvideo':
			case 'video/x-ms-wm':
			case 'video/x-ms-wmv':
			case 'video/x-ms-wmx':
			case 'video/x-ms-wvx':
			case 'video/quicktime':
				unset($headers['Content-Type']);
				$disposition = 'inline';
				break;

			// 音频。
			case 'audio/x-mei-aac':
			case 'audio/aiff':
			case 'audio/aiff':
			case 'audio/aiff':
			case 'audio/basic':
			case 'audio/x-liquid-file':
			case 'audio/x-liquid-secure':
			case 'audio/x-la-lms':
			case 'audio/mpegurl':
			case 'audio/mid':
			case 'audio/mid':
			case 'audio/x-musicnet-download':
			case 'audio/x-musicnet-stream':
			case 'audio/mp1':
			case 'audio/mp2':
			case 'audio/mp3':
			case 'audio/rn-mpeg':
			case 'audio/scpls':
			case 'audio/vnd.rn-realaudio':
			case 'audio/x-pn-realaudio':
			case 'audio/mid':
			case 'audio/x-pn-realaudio':
			case 'audio/x-pn-realaudio-plugin':
			case 'audio/basic':
			case 'audio/wav':
			case 'audio/x-ms-wax':
			case 'audio/x-ms-wma':
			case 'audio/scpls':
				$disposition = 'inline';
				break;
		}

		// 预先转换文件名到ASCII编码。
		$filename = mb_convert_encoding($filename, 'ASCII', 'UTF-8');

		// 生成指定Mime的数据响应。
		return response()->download($this->storage_path . $storage->path, $filename, $headers, $request->input('download', 'false') == 'true' ? 'attachment' : $disposition);
	}

	/**
	 * 上传文件
	 */
	public function postFile(Request $request, GetId3 $getId3)
	{
		// 验证数据。
		if (! $request->hasFile($this->filed_name)) {
			return response('必须提供 file 字段数据。', 402);
		}

		// 取得上传的文件。
		$file = $request->file($this->filed_name);

		// 获取文件ID3信息。
		$info = $getId3->analyze($file->getRealPath());

		// 修复ID3库对不支持的文件格式的处理。
		if (! isset($info['mime_type'])) {
			$info['mime_type'] = mime_content_type($file->getRealPath());
		}
		$info['fileformat'] = strtolower($file->getClientOriginalExtension());

		// 修复apk的mime。
		if (@$info['mime_type'] === 'application/zip' && @$info['fileformat'] === 'apk') {
			$info['mime_type'] = 'application/vnd.android.package-archive';
		}

		// 取得文件的hash与文件名。
		$hash = md5_file($file->getRealPath());
		$filename = $hash . '.' . $file->getClientOriginalExtension();

		// 取得hash的文件。
		$storage = Storage::find($hash);

		// 如果文件不存在，则创建文件。
		if (is_null($storage)) {
			$storage = new Storage();
			$storage->hash = $hash;
			$storage->format = @$info['fileformat'] ?  : '';
			$storage->size = @$info['filesize'] ?  : 0;
			$storage->width = @$info['video']['resolution_x'] ?  : 0;
			$storage->height = @$info['video']['resolution_y'] ?  : 0;
			$storage->seconds = @$info['playtime_seconds'] ?  : 0;
			$storage->mime = @$info['mime_type'] ?  : '';
			$storage->path = $filename;
			$storage->save();
			// 移动文件到存储目录
			$file->move($this->storage_path, $filename);
		}


		// 关联用户的文件。
		$userfile = new UserFile();
//         $user = is_null(Auth::client()->get()) ? Auth::admin()->get() : Auth::client()->get();
//         if(is_null($user)){
//             $userfile->user()->associate($user);
//         }
		$userfile->storage()->associate($storage);
		$userfile->filename = $file->getClientOriginalName();
		$userfile->save();

		// 返回结果。
		return [
			'file_id' => $userfile->id,
			'filename' => $userfile->filename,
			'url' => $userfile->url,
			'format' => $userfile->storage->format,
			'size' => $userfile->storage->size,
			'width' => $userfile->storage->width,
			'height' => $userfile->storage->height,
			'mime' => $userfile->storage->mime
		];
	}

	/**
	 * 使用文件 hash 上传文件
	 */
	public function postFileWithHash(Request $request)
	{
		// 验证数据。
		$this->validate($request, [
			'hash' => [
				'required',
				'exists:storage,hash'
			],
			'filename' => [
				'required'
			]
		]);

		// 取得提交的文件 hash。
		$hash = $request->input('hash');

		// 取得hash的文件。
		$storage = Storage::find($hash);

		// 关联用户的文件。
		$userfile = new UserFile();
// 		$userfile->user()->associate(Auth::client()->get());
		$userfile->storage()->associate($storage);
		$userfile->filename = $request->input('filename');
		$userfile->save();

		// 返回结果。
		return [
			'file_id' => $userfile->id,
			'filename' => $userfile->filename,
			'url' => $userfile->url,
			'format' => $userfile->storage->format,
			'size' => $userfile->storage->size,
			'width' => $userfile->storage->width,
			'height' => $userfile->storage->height,
			'mime' => $userfile->storage->mime
		];
	}
	
	
	/**
	 * Kedit上传文件
	 */
	public function postKeditFile(Request $request, GetId3 $getId3)
	{
		// 验证数据。
		if (! $request->hasFile($this->filed_name)) {
			return response('必须提供 file 字段数据。', 402);
		}
	
		// 取得上传的文件。
		$file = $request->file($this->filed_name);
	
		// 获取文件ID3信息。
		$info = $getId3->analyze($file->getRealPath());
	
		// 修复ID3库对不支持的文件格式的处理。
		if (! isset($info['mime_type'])) {
			$info['mime_type'] = mime_content_type($file->getRealPath());
		}
		$info['fileformat'] = $file->getClientOriginalExtension();
	
		// 取得文件的hash与文件名。
		$hash = md5_file($file->getRealPath());
		$filename = $hash . '.' . $file->getClientOriginalExtension();
	
		// 取得hash的文件。
		$storage = Storage::find($hash);
	
		// 如果文件不存在，则创建文件。
		if (is_null($storage)) {
			$storage = new Storage();
			$storage->hash = $hash;
			$storage->format = @$info['fileformat'] ?  : '';
			$storage->size = @$info['filesize'] ?  : 0;
			$storage->width = @$info['video']['resolution_x'] ?  : 0;
			$storage->height = @$info['video']['resolution_y'] ?  : 0;
			$storage->seconds = @$info['playtime_seconds'] ?  : 0;
			$storage->mime = @$info['mime_type'] ?  : '';
			$storage->path = $filename;
			$storage->save();
			// 移动文件到存储目录
			$file->move($this->storage_path, $filename);
		}
	
		// 关联用户的文件。
		$userfile = new UserFile();
		$userfile->user()->associate(Auth::admin()->get());
		$userfile->storage()->associate($storage);
		$userfile->filename = $file->getClientOriginalName();
		$userfile->save();
	
		// 返回结果。
		return  response()->json([
				'error' => 0,
				'url' => $userfile->url,
				'id' => $userfile->id
				]);
	}


	/**
	 * 上传文件
	 */
	public function postFileOperate(Request $request, GetId3 $getId3)
	{
		// 验证数据。
		if (! $request->hasFile($this->filed_name)) {
			return response('必须提供 file 字段数据。', 402);
		}

		// 取得上传的文件。
		$file = $request->file($this->filed_name);

		// 获取文件ID3信息。
		$info = $getId3->analyze($file->getRealPath());

		// 修复ID3库对不支持的文件格式的处理。
		if (! isset($info['mime_type'])) {
			$info['mime_type'] = mime_content_type($file->getRealPath());
		}
		$info['fileformat'] = strtolower($file->getClientOriginalExtension());

		// 修复apk的mime。
		if (@$info['mime_type'] === 'application/zip' && @$info['fileformat'] === 'apk') {
			$info['mime_type'] = 'application/vnd.android.package-archive';
		}

		// 取得文件的hash与文件名。
		$hash = md5_file($file->getRealPath());
		$filename = $hash . '.' . $file->getClientOriginalExtension();

		// 取得hash的文件。
		$storage = Storage::find($hash);

		// 如果文件不存在，则创建文件。
		if (is_null($storage)) {
			$storage = new Storage();
			$storage->hash = $hash;
			$storage->format = @$info['fileformat'] ?  : '';
			$storage->size = @$info['filesize'] ?  : 0;
			$storage->width = @$info['video']['resolution_x'] ?  : 0;
			$storage->height = @$info['video']['resolution_y'] ?  : 0;
			$storage->seconds = @$info['playtime_seconds'] ?  : 0;
			$storage->mime = @$info['mime_type'] ?  : '';
			$storage->path = $filename;
			$storage->save();
			// 移动文件到存储目录
			$file->move($this->storage_path, $filename);
		}


		// 关联用户的文件。
		$userfile = new UserFile();
		$userfile->storage()->associate($storage);
		$userfile->filename = $file->getClientOriginalName();
		$userfile->save();

		//关联文件上传记录
		$file_operate = new FileOperate();
		$file_operate->file_id = $userfile->id;
		$file_operate->file_name = $userfile->filename;
		$file_operate->member_id = Auth::client()->user()->id;
		$file_operate->type = 1;
		$file_operate->save();


		// 返回结果。
		return [
				'file_id' => $userfile->id,
				'filename' => $userfile->filename,
				'url' => $userfile->url,
				'format' => $userfile->storage->format,
				'size' => $userfile->storage->size,
				'width' => $userfile->storage->width,
				'height' => $userfile->storage->height,
				'mime' => $userfile->storage->mime
		];
	}

	public function postPluploadFile(Request $request,GetId3 $getId3){
        $this->filed_name = 'file';
        // 验证数据。
        if (! $request->hasFile($this->filed_name)) {
            return response('必须提供 file 字段数据。', 402);
        }

        // 取得上传的文件。
        $file = $request->file($this->filed_name);

        // 获取文件ID3信息。
        $info = $getId3->analyze($file->getRealPath());

        // 修复ID3库对不支持的文件格式的处理。
        if (! isset($info['mime_type'])) {
            $info['mime_type'] = mime_content_type($file->getRealPath());
        }
        $info['fileformat'] = $file->getClientOriginalExtension();

        // 取得文件的hash与文件名。
        $hash = md5_file($file->getRealPath());
        $filename = $hash . '.' . $file->getClientOriginalExtension();

        // 取得hash的文件。
        $storage = Storage::find($hash);

        // 如果文件不存在，则创建文件。
        if (is_null($storage)) {
            $storage = new Storage();
            $storage->hash = $hash;
            $storage->format = @$info['fileformat'] ?  : '';
            $storage->size = @$info['filesize'] ?  : 0;
            $storage->width = @$info['video']['resolution_x'] ?  : 0;
            $storage->height = @$info['video']['resolution_y'] ?  : 0;
            $storage->seconds = @$info['playtime_seconds'] ?  : 0;
            $storage->mime = @$info['mime_type'] ?  : '';
            $storage->path = $filename;
            $storage->save();
            // 移动文件到存储目录
            $file->move($this->storage_path, $filename);
        }

        // 关联用户的文件。
        $userfile = new UserFile();
        $userfile->user()->associate(Auth::admin()->get());
        $userfile->storage()->associate($storage);
        $userfile->filename = $file->getClientOriginalName();
        $userfile->save();

        // 返回结果。
        return  json_encode([
            'error' => 0,
            'url' => $userfile->url,
            'id' => $userfile->id,
            'name' => $filename,
            'hash' => $storage->hash
        ]);
    }

    public function postCkedit(Request $request,GetId3 $getId3){
        $this->filed_name = 'upload';
        // 验证数据。
        if (! $request->hasFile($this->filed_name)) {
            return response('必须提供 file 字段数据。', 402);
        }

        // 取得上传的文件。
        $file = $request->file($this->filed_name);

        // 获取文件ID3信息。
        $info = $getId3->analyze($file->getRealPath());

        // 修复ID3库对不支持的文件格式的处理。
        if (! isset($info['mime_type'])) {
            $info['mime_type'] = mime_content_type($file->getRealPath());
        }
        $info['fileformat'] = $file->getClientOriginalExtension();

        // 取得文件的hash与文件名。
        $hash = md5_file($file->getRealPath());
        $filename = $hash . '.' . $file->getClientOriginalExtension();

        // 取得hash的文件。
        $storage = Storage::find($hash);

        // 如果文件不存在，则创建文件。
        if (is_null($storage)) {
            $storage = new Storage();
            $storage->hash = $hash;
            $storage->format = @$info['fileformat'] ?  : '';
            $storage->size = @$info['filesize'] ?  : 0;
            $storage->width = @$info['video']['resolution_x'] ?  : 0;
            $storage->height = @$info['video']['resolution_y'] ?  : 0;
            $storage->seconds = @$info['playtime_seconds'] ?  : 0;
            $storage->mime = @$info['mime_type'] ?  : '';
            $storage->path = $filename;
            $storage->save();
            // 移动文件到存储目录
            $file->move($this->storage_path, $filename);
        }

        // 关联用户的文件。
        $userfile = new UserFile();
        $userfile->user()->associate(Auth::admin()->get());
        $userfile->storage()->associate($storage);
        $userfile->filename = $file->getClientOriginalName();
        $userfile->save();

        // 返回结果。
//        return  response()->json([
//            'error' => 0,
//            'url' => $userfile->url,
//            'id' => $userfile->id
//        ]);
        $callback  = $request->input('CKEditorFuncNum');
        $file_url = '/file/fetch?id='.$userfile->id;
        return "<script type=\"text/javascript\">window.parent.CKEDITOR.tools.callFunction('{$callback}','{$file_url}','')</script>";
    }
}
